Diese Tutorial wurde ursprünglich von Andreas Löffler geschrieben. Es hat zudem auch den ganzen HTML Kram für dieses Tutorial geschrieben. Ein paar Tage später hat mir Rob Fletcher eine Irix Version von Lektion 29 gemailt. In seiner Version hat er den größten Teil des Codes umgeschrieben. Deshalb habe ich Rob's Irix / GLUT Code nach Visual C++ / Win 32 portiert. Ich habe dann den Code der Nachrichtenschleife und des Fullscreen Codes modifziert. Wenn das Programm minimiert ist, sollte es 0% der CPU (oder fast so wenig) nutzen. Beim hin und her wechseln zum Fullscreen Modus sollten die meisten Probleme beseitigt worden sein (Screen wird nicht wieder richtig hergestellt, vermurkste Anzeige, etc).
Andreas Tutorial ist nun besser denn je. Unglücklicherweise hat sich der Code ein wenig geändert, so dass der HTML Kram dazu von mir komplett neugeschrieben wurde. Vielen Dank an Andreas, der den Ball ins rollen gebracht hat und sich einen Wolf gearbeitet hat, um dieses Killer-Tutorial zu machen. Dank an Rob für die Modifizierungen!
Lassen Sie uns beginnen... Wir erzeugen eine Device Modus Struktur namens DMsaved. Wir werden diese Struktur zum speichern der Informationen über die Benutzer Desktop Standard-Auflösung verwenden, wie Farbtiefe, etc., bevor wir zum Fullscreen Modus wechseln. Mehr darüber später! Beachten Sie, dass wir nur Speicherplatz für eine Textur alloziieren (texture[1]).
#include < windows.h> // Header Datei für Windows #include < gl\gl.h> // Header Datei für die OpenGL32 Library #include < gl\glu.h> // Header Datei für die GLu32 Library #include < stdio.h> // Header Datei für Datei Operationen HDC hDC=NULL; // Privater GDI Device Context HGLRC hRC=NULL; // Permanenter Rendering Context HWND hWnd=NULL; // Enthält unser Fenster-Handle HINSTANCE hInstance = NULL; // Enthält die Instanz der Applikation bool keys[256]; // Array das für die Tastatur Routine verwendet wird bool active=TRUE; // Fenster Aktiv Flag standardmäßig auf TRUE gesetzt bool fullscreen=TRUE; // Fullscreen Flag ist standardmäßig auf TRUE gesetzt DEVMODE DMsaved; // speichert die vorherigen Bildschirm Einstellungen (NEU) GLfloat xrot; // X Rotation GLfloat yrot; // Y Rotation GLfloat zrot; // Z Rotation GLuint texture[1]; // Speicherplatz für 1 Textur
typedef struct Texture_Image
{
int width; // Breite des Bildes in Pixeln
int height; // Hähe des Boldes in Pixeln
int format; // Anzahl der Bytes pro Pixel
unsigned char *data; // Textur Daten
} TEXTURE_IMAGE;
typedef TEXTURE_IMAGE *P_TEXTURE_IMAGE; // Ein Zeiger auf den Textur Image-Datentyp P_TEXTURE_IMAGE t1; // Zeiger auf den Textur Image Datentyp P_TEXTURE_IMAGE t2; // Zeiger auf den Textur Image Datentyp LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Deklaration für WndProc
// alloziiere eine Image-Struktur und alloziiere darin den benötigten Speicher
P_TEXTURE_IMAGE AllocateTextureBuffer( GLint w, GLint h, GLint f)
{
P_TEXTURE_IMAGE ti=NULL; // Zeiger auf Image Struct
unsigned char *c=NULL; // Zeiger auf Speicherblock für's Image
ti = (P_TEXTURE_IMAGE)malloc(sizeof(TEXTURE_IMAGE)); // eine Image Struktur bitte
if( ti != NULL ) {
ti->width = w; // Setze Breite
ti->height = h; // Setze Höhe
ti->format = f; // Setze Format
c = (unsigned char *)malloc( w * h * f);
if ( c != NULL ) {
ti->data = c;
}
else {
MessageBox(NULL,"Could Not Allocate Memory For A Texture Buffer","BUFFER ERROR",MB_OK | MB_ICONINFORMATION);
return NULL;
}
}
else
{
MessageBox(NULL,"Could Not Allocate An Image Structure","IMAGE STRUCTURE ERROR",MB_OK | MB_ICONINFORMATION);
return NULL;
}
return ti; // gebe Zeiger auf Image Struct zurück
}
// Gebe Image Daten frei
void DeallocateTexture( P_TEXTURE_IMAGE t )
{
if(t)
{
if(t->data)
{
free(t->data); // gebe seinen Image Buffer frei
}
free(t); // gebe den Rest frei
}
}
// lese eine .RAW Datei in den alloziierten Image Buffer ein, unter der Benutzung der Daten im Image Struktur Header.
// Flippe das Image von unten nach oben. Gebe 0 für einen Fehler beim Lesen zurück oder die Anzahl gelesener Bytes.
int ReadTextureData ( char *filename, P_TEXTURE_IMAGE buffer)
{
FILE *f;
int i,j,k,done=0;
int stride = buffer->width * buffer->format; // Größe einer Zeile (Breite * Bytes pro Pixel)
unsigned char *p = NULL;
f = fopen(filename, "rb"); // öffne "filename" um Bytes zu lesen
if( f != NULL ) // wenn Datei existiert
{
for( i = buffer->height-1; i >= 0 ; i-- ) // durchlaufe die Höhe (von unten nach oben - Flippe Image)
{
p = buffer->data + (i * stride );
for ( j = 0; j < buffer->width ; j++ ) // durchlaufe die Breite
{
for ( k = 0 ; k < buffer->format-1 ; k++, p++, done++ )
{
*p = fgetc(f); // lese Wert aus der Datei und speicher diesen im Speicher
}
*p = 255; p++; // Speichere 255 im Alpha Channel und inkrementiere Zeiger
}
}
fclose(f); // schließe die Datei
}
else // ansonsten
{
MessageBox(NULL,"Unable To Open Image File","IMAGE ERROR",MB_OK | MB_ICONINFORMATION);
}
return done; // gebe die Anzahl der gelesenen Bytes zurück
}
void BuildTexture (P_TEXTURE_IMAGE tex)
{
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, tex->width, tex->height, GL_RGBA, GL_UNSIGNED_BYTE, tex->data);
}
void Blit( P_TEXTURE_IMAGE src, P_TEXTURE_IMAGE dst, int src_xstart, int src_ystart, int src_width, int src_height,
int dst_xstart, int dst_ystart, int blend, int alpha)
{
int i,j,k;
unsigned char *s, *d; // Quelle & Ziel
// korrigiere Alpha wenn Wert außerhalb der Grenze liegt
if( alpha > 255 ) alpha = 255;
if( alpha < 0 ) alpha = 0;
// überprüfe auf inkorrekte Blend Flag Werte
if( blend < 0 ) blend = 0;
if( blend > 1 ) blend = 1;
d = dst->data + (dst_ystart * dst->width * dst->format); // Start Zeile - dst (Zeile * Breite in Pixel * Bytes Pro Pixel)
s = src->data + (src_ystart * src->width * src->format); // Start Zeile - src (Zeile * Breite In Pixels * Bytes Pro Pixel)
for (i = 0 ; i < src_height ; i++ ) // Höhen-Schleife
{
s = s + (src_xstart * src->format); // bewege durch Src Daten um Bytes Pro Pixel
d = d + (dst_xstart * dst->format); // bewege durchDst Daten um Bytes Pro Pixel
for (j = 0 ; j < src_width ; j++ ) // Breiten-Schleife
{
for( k = 0 ; k < src->format ; k++, d++, s++) // "n" Bytes auf einmal
{
if (blend) // wenn Blending eingeschaltet ist
*d = ( (*s * alpha) + (*d * (255-alpha)) ) >> 8; // Multipliziere Src Daten*Alpha addiere Dst Daten*(255-alpha)
else // in der 0-255 Grenze bleiben mittels >> 8
*d = *s; // kein Blending einfach nur eine einfache Kopie
}
}
d = d + (dst->width - (src_width + dst_xstart))*dst->format; // füge Ende der Zeile an
s = s + (src->width - (src_width + src_xstart))*src->format; // füge Ende der Zeile an
}
}
int InitGL(GLvoid) // Dies wird direkt nachdem das GL Fenster erzeugt wurde, aufgerufen
{
t1 = AllocateTextureBuffer( 256, 256, 4 ); // hole eine Image Struktur
if (ReadTextureData("Data/Monitor.raw",t1)==0) // Fülle die Image Struktur mit Daten
{ // nichts gelesen?
MessageBox(NULL,"Could Not Read 'Monitor.raw' Image Data","TEXTURE ERROR",MB_OK | MB_ICONINFORMATION);
return FALSE;
}
t2 = AllocateTextureBuffer( 256, 256, 4 ); // zweite Image Struktur
if (ReadTextureData("Data/GL.raw",t2)==0) // fülle die Image Struktur mit Daten
{ // nichts gelesen?
MessageBox(NULL,"Could Not Read 'GL.raw' Image Data","TEXTURE ERROR",MB_OK | MB_ICONINFORMATION);
return FALSE;
}
// Image in das geblendet werden soll, Original Image, Src Start X & Y, Src Breite & Höhe, Dst Location X & Y, Blend Flag, Alpha Wert Blit(t2,t1,127,127,128,128,64,64,1,127); // rufe dieBlitter Routine auf
BuildTexture (t1); // Lade die Textur Map in den Textur Speicher DeallocateTexture( t1 ); // lösche den Image Speicher, da die Textur nun DeallocateTexture( t2 ); // im GL Textur Speicher ist glEnable(GL_TEXTURE_2D); // aktiviert Textur Mapping glShadeModel(GL_SMOOTH); // aktiviert Smooth Color Shading glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // dies löscht Schwarzer den Hintergrund auf schwarz glClearDepth(1.0); // aktiviert die Löschung des Depth Buffers glEnable(GL_DEPTH_TEST); // aktiviert Depth Testing glDepthFunc(GL_LESS); // Die Art des auszuführenden Depth Test return TRUE; }
GLvoid DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Lösche den Bildschirm und den Depth-Buffer
glLoadIdentity(); // Resette die View
glTranslatef(0.0f,0.0f,-5.0f);
glRotatef(xrot,1.0f,0.0f,0.0f);
glRotatef(yrot,0.0f,1.0f,0.0f);
glRotatef(zrot,0.0f,0.0f,1.0f);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glBegin(GL_QUADS);
// vordere Seite
glNormal3f( 0.0f, 0.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
// hintere Seite
glNormal3f( 0.0f, 0.0f,-1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
// obere Seite
glNormal3f( 0.0f, 1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
// untere Seite
glNormal3f( 0.0f,-1.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
// rechte Seite
glNormal3f( 1.0f, 0.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
// linke Seite
glNormal3f(-1.0f, 0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
glEnd();
xrot+=0.3f;
yrot+=0.2f;
zrot+=0.4f;
}
GLvoid KillGLWindow(GLvoid) // Entferne das Fenster korrekt
{
if (fullscreen) // Sind wir im Fullscreen Modus?
{
if (!ChangeDisplaySettings(NULL,CDS_TEST)) { // wenn der Shortcut nicht funktioniert
ChangeDisplaySettings(NULL,CDS_RESET); // mache es trotzdem (um die Werte aus der Registry zu holen)
ChangeDisplaySettings(&DMsaved,CDS_RESET); // ändere sie auf die gespeicherten Einstellungen
}
else // kein Fullscreen
{
ChangeDisplaySettings(NULL,CDS_RESET); // mache nichts
}
ShowCursor(TRUE); // Zeige den Maus-Zeiger
}
if (hRC) // Haben wir einen Rendering Context?
{
if (!wglMakeCurrent(NULL,NULL)) // Können wir den DC und RC Kontext freigeben?
{
MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
if (!wglDeleteContext(hRC)) // Können wir den RC löschen?
{
MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
hRC=NULL; // Setze RC auf NULL
}
if (hDC && !ReleaseDC(hWnd,hDC)) // Können wir DC freigeben?
{
MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hDC=NULL; // Setze DC auf NULL
}
if (hWnd && !DestroyWindow(hWnd)) // Können wir das Fenster zerstören?
{
MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hWnd=NULL; // Setze hWnd auf NULL
}
if (!UnregisterClass("OpenGL",hInstance)) // Können wir die Klasse de-registrieren?
{
MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hInstance=NULL; // Setze hInstance auf NULL
}
}
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
GLuint PixelFormat; // Enthält die Ergebnisse nachdem nach was passendem gesucht wurde
WNDCLASS wc; // Fenster Klassen Struktur
DWORD dwExStyle; // erweiterter Fenster-Stil
DWORD dwStyle; // Fenster-Stil
fullscreen=fullscreenflag; // Setze das globale Fullscreen Flag
hInstance = GetModuleHandle(NULL); // Ermittle die Instanz für unser Fenster
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Zeichne neu beim bewegen und eigener DC für's Fenster
wc.lpfnWndProc = (WNDPROC) WndProc; // WndProc behandelt die Nachrichten
wc.cbClsExtra = 0; // Keine extra Fenster-Daten
wc.cbWndExtra = 0; // Keine extra Fenster-Daten
wc.hInstance = hInstance; // Setze die Instanz
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Lade das Standard-Icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Lade den Pfeil-Zeiger
wc.hbrBackground = NULL; // es wird kein Hintergrund für GL benötigt
wc.lpszMenuName = NULL; // Wir wollen kein Menü
wc.lpszClassName = "OpenGL"; // Setze den Klassen Namen
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DMsaved); // sichere den aktuellen Anzeige Status (NEU)
if (fullscreen) // Fullscreen Modus?
{
DEVMODE dmScreenSettings; // Device Modus
memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // stelle sicher, dass der Speicher gelöscht ist
dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Größe derDevmode Structure
dmScreenSettings.dmPelsWidth = width; // ausgewählte Bildschirm Breite
dmScreenSettings.dmPelsHeight = height; // ausgewählte Bildschirm Höhe
dmScreenSettings.dmBitsPerPel = bits; // ausgewählte Bits Pro Pixel
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
// Versuche den gewählten Modus zu setzen und hole Ergebnisse. ANMERKUNG: CDS_FULLSCREEN lässt die Start Bar nicht anzeigen.
if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
{
// wenn der Modus fehl schlug, schlage zwei Alternativen vor. Beenden oder Fenster-Modus.
if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported
By\nYour Video Card. Use Windowed Mode Instead?","NeHe
GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
fullscreen=FALSE; // Fenster-Modus gewählt. Fullscreen = FALSE
}
else
{
// zeige Message Box an, die den Benutzer wissen lässt, dass das Programm geschlossen wird.
MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
return FALSE; // gebeFALSE zurück
}
}
}
int WINAPI WinMain( HINSTANCE hInstance, // Instanz
HINSTANCE hPrevInstance, // vorherige Instanz
LPSTR lpCmdLine, // Kommandozeilen Parameter
int nCmdShow) // Fenster Anzeige Status
{
MSG msg; // Windows Nachrichten Struktur
BOOL done=FALSE; // Bool Variable um die Schleife zu beenden
// Frage den Benutzer, in welchen Modus er starten will
if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE; // Fenster-Modus
}
// erzeuge unser OpenGL Fenster
if (!CreateGLWindow("Andreas Löffler, Rob Fletcher & NeHe's Blitter
& Raw Image Loading Tutorial", 640, 480, 32, fullscreen))
{
return 0; // Beende, wenn Fenster nicht erzeugt wurde
}
while(!done) // Schleife die so lange läuft, solange done=FALSE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Wartet eine Nachricht?
{
if (msg.message==WM_QUIT) // Haben wir eine Nachricht zum beenden erhalten?
{
done=TRUE; // Wenn ja done=TRUE
}
else // Wenn nicht, bearbeite die Fenster-Nachrichten
{
TranslateMessage(&msg); // Übersetze die Nachricht
DispatchMessage(&msg); // bearbeite die Nachricht
}
}
if (!active) // Programm inaktiv?
{
WaitMessage(); // Warte auf Message / Mache nichts ( NEU ... Danke Jim Strong )
}
if (keys[VK_ESCAPE]) // Wurde ESC gedrückt?
{
done=TRUE; // ESC Signalisiert, dass Beendet werden soll
}
if (keys[VK_F1]) // Wurde F1 gedrückt?
{
keys[VK_F1]=FALSE; // Wenn ja, setze Taste auf FALSE
KillGLWindow(); // Kill unser aktuelles Fenster
fullscreen=!fullscreen; // Wechsel zwischen Fullscreen und Fester-Modus
// Erzeuge unser OpenGL Fenster erneut
if (!CreateGLWindow("Andreas Löffler, Rob Fletcher & NeHe's
Blitter & Raw Image Loading Tutorial",640,480,16,fullscreen))
{
return 0; // Beenden, wenn das Fenster nicht erzeugt wurde
}
}
DrawGLScene(); // Zeichne die Szene
SwapBuffers(hDC); // Swap Buffers (Double Buffering)
}
// Shutdown
KillGLWindow(); // Kill das Fenster
return (msg.wParam); // Beende das Programm
}